﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using UnityEngine;

// Translate: Finished
// Debug: 

namespace Assets.Scripts.LDS
{
    sealed class RelationModelManager
    {
        /// <summary>
        /// Prevents a default instance of the 
        /// <see cref="Singleton"/> class from being created.
        /// </summary>
        private static RelationModelManager _instance = null;

        /// <summary>
        /// Gets the instance.
        /// </summary>
        public static RelationModelManager Instance
        {
            get { return _instance ?? (_instance = new RelationModelManager()); }
        }

        static string model_pth = Params.relationModelPath;

        public Dictionary<string, PairwiseRelationModel> m_relativeModels = new Dictionary<string, PairwiseRelationModel>();  // all relative models with general relations
        public Dictionary<string, PairwiseRelationModel> m_pairwiseRelModels = new Dictionary<string, PairwiseRelationModel>();  // all pairwise relation models
        public Dictionary<string, GroupRelationModel> m_groupRelModels = new Dictionary<string, GroupRelationModel>();  // all group relations
        public Dictionary<string, SupportRelation> m_supportRelations = new Dictionary<string, SupportRelation>();
        public Dictionary<string, SupportProb> m_suppProbs = new Dictionary<string, SupportProb>();
        public Dictionary<string, CoOccurrenceModel> m_coOccModelsOnSameParent = new Dictionary<string, CoOccurrenceModel>();
        public Dictionary<string, CoOccurrenceModel> m_coOccModelsInSameGroup = new Dictionary<string, CoOccurrenceModel>();

        public List<string> m_pairwiseModelKeys = new List<string>();

        public double m_closeSampleTh;  // threshold for avoiding close sample
        public double m_sceneMetric;

        public List<string> m_adjustFrontObjs = new List<string>();

        public const double ExWeight = 0.7;

        private RelationModelManager()
        {
            loadRelationModels();

            m_sceneMetric = Params.globalSceneUnitScale; 
            m_closeSampleTh = 0.1 / m_sceneMetric;

            m_adjustFrontObjs.Add("desk");
            m_adjustFrontObjs.Add("bookcase");
            m_adjustFrontObjs.Add("cabinet");
            m_adjustFrontObjs.Add("dresser");
            m_adjustFrontObjs.Add("monitor");
            m_adjustFrontObjs.Add("tv");
            m_adjustFrontObjs.Add("bed");
        }


        public void loadRelationModels()
        {
            System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
            stopwatch.Start(); // Start to Monitor runtime

            TextDialog.textDebug("Relational models loading...");

            loadRelativeRelationModels();
            loadPairwiseRelationModels();
            loadGroupRelationModels();
            loadSupportRelationModels();
            loadCoOccurenceModels();

            stopwatch.Stop(); //  Stop monitoring
            TimeSpan timespan = stopwatch.Elapsed; //  Gets the total time measured by the current instance
            double seconds = timespan.TotalSeconds;  //  Total seconds

            TextDialog.textDebug("Relational models loaded in " + seconds + "s");

        }
        public void loadRelativeRelationModels()
        {
            string currLine;
            using (StreamReader sr = new StreamReader(model_pth + "Relative_release.model"))
            {
                while (sr.Peek() != -1)
                {
                    currLine = sr.ReadLine();
                    List<string> parts = Util.PartitionString(currLine, "_");

                    string anchorObjName = parts[0];
                    string actObjName = parts[1];
                    string conditionName = parts[2];
                    string relationName = parts[3];

                    PairwiseRelationModel newRelModel = new PairwiseRelationModel(anchorObjName, actObjName, conditionName, relationName);
                    m_relativeModels[newRelModel.m_relationKey] = newRelModel;

                    newRelModel.loadFromStream(sr);
                }
            }

        }
        public void loadPairwiseRelationModels()
        {
            string currLine;
            using (StreamReader sr = new StreamReader(model_pth + "Pairwise_release.model"))
            {
                while (sr.Peek() != -1)
                {
                    currLine = sr.ReadLine();
                    List<string> parts = Util.PartitionString(currLine, "_");

                    string anchorObjName = parts[0];
                    string actObjName = parts[1];
                    string conditionName = parts[2];
                    string relationName = parts[3];

                    PairwiseRelationModel newRelModel = new PairwiseRelationModel(anchorObjName, actObjName, conditionName, relationName);
                    m_pairwiseRelModels[newRelModel.m_relationKey] = newRelModel;

                    newRelModel.loadFromStream(sr);
                }
            }
            loadPairModelSim();
        }
        public void loadGroupRelationModels()
        {
            string currLine;
            using (StreamReader sr = new StreamReader(model_pth + "Group_release.model"))
            {
                while (sr.Peek() != -1)
                {
                    currLine = sr.ReadLine();
                    List<string> parts = Util.PartitionString(currLine, ",");
                    List<string> subParts = Util.PartitionString(parts[0], "_");

                    string relationName = subParts[0];
                    string anchorObjName = subParts[1];

                    GroupRelationModel newGroupModel = new GroupRelationModel(anchorObjName, relationName);
                    newGroupModel.m_numInstance = int.Parse(parts[1]);
                    m_groupRelModels[newGroupModel.m_groupKey] = newGroupModel;

                    currLine = sr.ReadLine();
                    if (currLine.Contains("occurrence"))
                    {
                        int occurModelNum = Util.StringToIntegerList(currLine, "occurrence ")[0];
                        for (int i = 0; i < occurModelNum; i++)
                        {
                            currLine = sr.ReadLine();
                            parts = Util.PartitionString(currLine, ",");
                            subParts = Util.PartitionString(parts[0], "_");
                            string objName = subParts[0];

                            if (isToSkipModelCats(objName)) continue;

                            int objnum = int.Parse(subParts[1]);
                            OccurrenceModel newOccurModel = new OccurrenceModel(objName, ref objnum);
                            newOccurModel.m_occurProb = float.Parse(parts[1]);
                            newGroupModel.m_occurModels[newOccurModel.m_occurKey] = newOccurModel;
                        }
                    }
                    currLine = sr.ReadLine();
                    if (currLine.Contains("pairwise"))
                    {
                        int pairModelNum = Util.StringToIntegerList(currLine, "pairwise ")[0];
                        for (int i = 0; i < pairModelNum; i++)
                        {
                            currLine = sr.ReadLine();
                            parts = Util.PartitionString(currLine, ",");
                            subParts = Util.PartitionString(parts[1], "_");

                            anchorObjName = subParts[0];
                            string actObjName = subParts[1];
                            string conditionName = subParts[2];
                            relationName = subParts[3];

                            PairwiseRelationModel newRelModel = new PairwiseRelationModel(anchorObjName, actObjName, conditionName, relationName);
                            newGroupModel.m_pairwiseModels[newRelModel.m_relationKey] = newRelModel;

                            newRelModel.loadFromStream(sr);
                        }
                    }

                    newGroupModel.computeMaxOccProbs();
                }
            }

            loadGroupPariModelSim();
        }
        public void loadPairModelSim()
        {
            string currLine;
            using (StreamReader sr = new StreamReader(model_pth + "Pairwise_release.sim"))
            {
                m_pairwiseModelKeys.Clear();
                for (int i = 0; i < m_pairwiseRelModels.Count; i++)
                {
                    currLine = sr.ReadLine();
                    List<string> parts = Util.PartitionString(currLine, ",");
                    List<string> subparts = Util.PartitionString(parts[1], "_");
                    PairwiseRelationModel relModel = m_pairwiseRelModels[parts[0]];
                    relModel.m_modelId = int.Parse(subparts[1]);
                    m_pairwiseModelKeys.Add(relModel.m_relationKey);

                    currLine = sr.ReadLine();
                    parts = Util.PartitionString(currLine, ",");
                    int simModelNum = int.Parse(parts[0]);
                    List<int> intList = Util.StringToIntegerList(parts[1], "");
                    relModel.m_simModelIds.Clear();

                    for (int k = 0; k < simModelNum; k++)
                    {
                        relModel.m_simModelIds.Add(intList[k]);
                    }

                    relModel.m_maxObjFeatures.Clear();
                    relModel.m_avgObjFeatures.Clear();
                    for (int m = 0; m < 2; m++)
                    {
                        currLine = sr.ReadLine();
                        relModel.m_maxObjFeatures.Add(Util.StringToFloatList(currLine, ""));
                    }

                    for (int m = 0; m < 2; m++)
                    {
                        currLine = sr.ReadLine();
                        relModel.m_avgObjFeatures.Add(Util.StringToFloatList(currLine, ""));
                    }
                }
            }

        }
        public void loadGroupPariModelSim()
        {
            string currLine;
            using (StreamReader sr = new StreamReader(model_pth + "Group_release.sim"))
            {
                for (int i = 0; i < m_groupRelModels.Count; i++)
                {
                    currLine = sr.ReadLine();
                    GroupRelationModel groupModel = m_groupRelModels[currLine];

                    groupModel.m_pairwiseModelKeys.Clear();
                    for (int p = 0; p < groupModel.m_pairwiseModels.Count; p++)
                    {
                        currLine = sr.ReadLine();
                        List<string> parts = Util.PartitionString(currLine, ",");
                        List<string> subparts = Util.PartitionString(parts[1], "_");
                        PairwiseRelationModel relModel = groupModel.m_pairwiseModels[parts[0]];
                        relModel.m_modelId = int.Parse(subparts[1]);
                        groupModel.m_pairwiseModelKeys.Add(relModel.m_relationKey);

                        currLine = sr.ReadLine();
                        parts = Util.PartitionString(currLine, ",");
                        int simModelNum = int.Parse(parts[0]);
                        List<int> intList = Util.StringToIntegerList(parts[1], "");
                        relModel.m_simModelIds.Clear();

                        for (int k = 0; k < simModelNum; k++)
                        {
                            relModel.m_simModelIds.Add(intList[k]);
                        }
                    }
                }
            }

        }
        public void loadSupportRelationModels()
        {
            string currLine;
            using (StreamReader sr = new StreamReader(model_pth + "SupportRelation_release.model"))
            {
                while (sr.Peek() != -1)
                {
                    SupportRelation newSuppRelation = new SupportRelation();
                    newSuppRelation.loadFromStream(sr);

                    m_supportRelations[newSuppRelation.m_suppRelKey] = newSuppRelation;
                }
            }

            using (StreamReader sr = new StreamReader(model_pth + "SupportParent_release.prob"))
            {

                while (sr.Peek() != -1)
                {
                    currLine = sr.ReadLine();
                    List<string> parts = Util.PartitionString(currLine, ",");
                    List<float> floatList = Util.StringToFloatList(parts[1], "");

                    SupportProb newSuppProb = new SupportProb((double)floatList[0], (double)floatList[1]);

                    m_suppProbs[parts[0]] = newSuppProb;
                }
            }
        }
        public void loadCoOccurenceModels()
        {
            using (StreamReader sr = new StreamReader(model_pth + "CoOccOnParent_release.model"))
            {
                while (sr.Peek() != -1)
                {
                    CoOccurrenceModel newCoOccModel = new CoOccurrenceModel();
                    newCoOccModel.loadFromStream(sr);
                    m_coOccModelsOnSameParent[newCoOccModel.m_coOccurKey] = newCoOccModel;
                }
            }
            using (StreamReader sr = new StreamReader(model_pth + "CoOccInGroup_release.model"))
            {
                while (sr.Peek() != -1)
                {
                    CoOccurrenceModel newCoOccModel = new CoOccurrenceModel();
                    newCoOccModel.loadFromStream(sr);
                    m_coOccModelsInSameGroup[newCoOccModel.m_coOccurKey] = newCoOccModel;
                }
            }

        }
        public bool isToSkipModelCats(string objName)
        {
            if (objName == "rug")
            {
                return true;
            }

            return false;
        }

        public class SupportProb
        {
            public SupportProb()
            {
                beParentProb = 0; beChildProb = 0;
            }
            public SupportProb(double parentProb, double childProb)
            {
                beParentProb = parentProb; beChildProb = childProb;
            }

            public double beParentProb;
            public double beChildProb;
        };

        public double getCoOccProbOnParent(string firstObjName, string seondObjName, string parentName)
        {
            double coOccProb = 0;
            string coOccKey = string.Format("{0}_{1}_{2}_{3}", firstObjName, seondObjName, "sibling", parentName);

            if (m_coOccModelsOnSameParent.ContainsKey(coOccKey))
            {
                coOccProb = m_coOccModelsOnSameParent[coOccKey].m_prob;
            }

            return coOccProb;
        }


    }

}
